/*!	 valuenode.cpp
**	 Valuenodes
**
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**	Copyright (c) 2007, 2008 Chris Moore
**	Copyright (c) 2008, 2011 Carlos López
**	Copyright (c) 2016 caryoscelus
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include "valuenode.h"
#include "valuenode_registry.h"
#include "general.h"
#include <synfig/localization.h>
#include "canvas.h"
#include "layer.h"

#endif

using namespace std;
using namespace etl;
using namespace synfig;

static int value_node_count(0);

ValueNode::LooseHandle
synfig::find_value_node(const GUID& guid)
{
    return guid_cast<ValueNode>(guid);
}

void
ValueNode::breakpoint()
{
    return;
}

ValueNode::ValueNode(Type &type): type(&type)
{
    value_node_count++;
}

bool
LinkableValueNode::set_link(int i, ValueNode::Handle x)
{
    ValueNode::Handle previous(get_link(i));

    if (set_link_vfunc(i, x)) {
        // Fix 2412072: remove the previous link from the parent_set unless one of the other links is also
        // using it when we convert a value to 'switch', both 'on' and 'off' are linked to the same valuenode
        // if we then disconnect one of the two, the one we disconnect is set to be a new valuenode_const
        // and the previously shared value is removed from the parent set even though the other is still
        // using it
        if (previous) {
            int size = link_count(), index;

            for (index = 0; index < size; ++index) {
                if (i == index) {
                    continue;
                }

                if (get_link(index) == previous) {
                    break;
                }
            }

            if (index == size) {
                remove_child(previous.get());
            }
        }

        add_child(x.get());

        if (!x->is_exported() && get_parent_canvas()) {
            x->set_parent_canvas(get_parent_canvas());
        }

        changed();
        return true;
    }

    return false;
}

ValueNode::LooseHandle
LinkableValueNode::get_link(int i)const
{
    return get_link_vfunc(i);
}

void
LinkableValueNode::unlink_all()
{
    for (int i = 0; i < link_count(); i++) {
        ValueNode::LooseHandle value_node(get_link(i));

        if (value_node) {
            value_node->parent_set.erase(this);
        }
    }
}

ValueNode::~ValueNode()
{
    value_node_count--;

    begin_delete();
}

void
ValueNode::on_changed()
{
    if (getenv("SYNFIG_DEBUG_ON_CHANGED")) {
        printf("%s:%d ValueNode::on_changed()\n", __FILE__, __LINE__);
    }

    etl::loose_handle<Canvas> parent_canvas = get_parent_canvas();

    if (parent_canvas)
        do {					// signal to all the ancestor canvases
            parent_canvas->signal_value_node_changed()(this);
        } while ((parent_canvas = parent_canvas->parent()));
    else if (get_root_canvas()) {
        get_root_canvas()->signal_value_node_changed()(this);
    }

    Node::on_changed();
}

int
ValueNode::replace(etl::handle<ValueNode> x)
{
    if (x.get() == this) {
        return 0;
    }

    while (parent_set.size()) {
        (*parent_set.begin())->add_child(x.get());
        (*parent_set.begin())->remove_child(this);
    }

    int r(RHandle(this).replace(x));
    x->changed();
    return r;
}

void
ValueNode::set_id(const String &x)
{
    if (name != x) {
        name = x;
        signal_id_changed_();
    }
}

String
ValueNode::get_description(bool show_exported_name)const
{
    if (dynamic_cast<const LinkableValueNode*>(this)) {
        return dynamic_cast<const LinkableValueNode*>(this)->get_description(-1, show_exported_name);
    }

    String ret(_("ValueNode"));

    if (dynamic_cast<const LinkableValueNode*>(this)) {
        return (dynamic_cast<const LinkableValueNode*>(this))->get_description(-1, show_exported_name);
    }

    if (show_exported_name && !is_exported()) {
        show_exported_name = false;
    }

    if (show_exported_name) {
        ret += strprintf(" (%s)", get_id().c_str());
    }

    return ret;
}

bool
ValueNode::is_descendant(ValueNode::Handle value_node_dest)
{
    if (!value_node_dest) {
        return false;
    }

    if (Handle(this) == value_node_dest) {
        return true;
    }

    // loop through the parents of each node in current_nodes
    set<Node*> node_parents(value_node_dest->parent_set);
    ValueNode::Handle value_node_parent = NULL;

    for (set<Node*>::iterator iter = node_parents.begin(); iter != node_parents.end(); iter++) {
        value_node_parent = ValueNode::Handle::cast_dynamic(*iter);

        if (Handle(this) == value_node_parent) {
            break;
        }
    }

    return value_node_dest->parent_count() ? is_descendant(value_node_parent) : false;
}

void
ValueNode::get_values(std::set<ValueBase> &x) const
{
    std::map<Time, ValueBase> v;
    get_values(v);

    for (std::map<Time, ValueBase>::const_iterator i = v.begin(); i != v.end(); ++i) {
        x.insert(i->second);
    }
}

void
ValueNode::get_value_change_times(std::set<Time> &x) const
{
    std::map<Time, ValueBase> v;
    get_values(v);

    for (std::map<Time, ValueBase>::const_iterator i = v.begin(); i != v.end(); ++i) {
        x.insert(i->first);
    }
}

void
ValueNode::get_values(std::map<Time, ValueBase> &x) const
{
    get_values_vfunc(x);
}

int
ValueNode::time_to_frame(Time t, Real fps)
{
    return (int)floor(t * fps + 1e-10);
}

int
ValueNode::time_to_frame(Time t)
{
    if (Canvas::Handle canvas = get_parent_canvas()) {
        const RendDesc &desc = canvas->rend_desc();
        return time_to_frame(t, desc.get_frame_rate());
    }

    return 0;
}

void
ValueNode::add_value_to_map(std::map<Time, ValueBase> &x, Time t, const ValueBase &v)
{
    std::map<Time, ValueBase>::const_iterator j = x.upper_bound(t);

    if (j == x.begin() || (--j)->second != v) {
        ValueBase tmp(v);
        x[t] = tmp;
    }
}

void
ValueNode::canvas_time_bounds(const Canvas &canvas, bool &found, Time &begin, Time &end, Real &fps)
{
    if (!found) {
        found = true;
        begin = canvas.rend_desc().get_time_start();
        end = canvas.rend_desc().get_time_end();
        fps = canvas.rend_desc().get_frame_rate();
    } else {
        begin = min(begin, canvas.rend_desc().get_time_start());
        end = min(end, canvas.rend_desc().get_time_end());
        fps = max(fps, (Real)canvas.rend_desc().get_frame_rate());
    }
}

void
ValueNode::find_time_bounds(const Node &node, bool &found, Time &begin, Time &end, Real &fps)
{
    for (std::set<Node*>::const_iterator i = node.parent_set.begin(); i != node.parent_set.end(); ++i) {
        if (!*i) {
            continue;
        }

        if (Layer *layer = dynamic_cast<Layer*>(*i)) {
            if (Canvas::Handle canvas = layer->get_canvas()) {
                canvas_time_bounds(*canvas->get_root(), found, begin, end, fps);
            }
        } else {
            find_time_bounds(**i, found, begin, end, fps);
        }
    }
}

void
ValueNode::calc_time_bounds(int &begin, int &end, Real &fps) const
{
    bool found = false;
    Time b = 0.0;
    Time e = 10 * 60;
    fps = 24;

    find_time_bounds(*this, found, b, e, fps);

    if (get_parent_canvas()) {
        canvas_time_bounds(*get_parent_canvas(), found, b, e, fps);
    }

    if (get_root_canvas()) {
        canvas_time_bounds(*get_root_canvas(), found, b, e, fps);
    }

    begin = (int)floor(b * fps);
    end = (int)ceil(e * fps);
}

void
ValueNode::calc_values(std::map<Time, ValueBase> &x, int begin, int end, Real fps) const
{
    if (fabs(fps) > 1e-10) {
        Real k = 1.0 / fps;

        if (begin > end) {
            swap(begin, end);
        }

        for (int i = begin; i <= end; ++i) {
            add_value_to_map(x, i * k, (*this)(i * k));
        }
    }
}

void
ValueNode::calc_values(std::map<Time, ValueBase> &x, int begin, int end) const
{
    int b, e;
    Real fps;
    calc_time_bounds(b, e, fps);
    calc_values(x, begin, end, fps);
}

void
ValueNode::calc_values(std::map<Time, ValueBase> &x) const
{
    int begin, end;
    Real fps;
    calc_time_bounds(begin, end, fps);
    calc_values(x, begin, end, fps);
}

void
ValueNode::get_values_vfunc(std::map<Time, ValueBase> &x) const
{
    calc_values(x);
}

ValueNodeList::ValueNodeList():
    placeholder_count_(0)
{
}

bool
ValueNodeList::count(const String &id)const
{
    const_iterator iter;

    if (id.empty()) {
        return false;
    }

    for (iter = begin(); iter != end() && id != (*iter)->get_id(); ++iter)
        ;

    if (iter == end()) {
        return false;
    }

    return true;
}

ValueNode::Handle
ValueNodeList::find(const String &id, bool might_fail)
{
    iterator iter;

    if (id.empty()) {
        throw Exception::IDNotFound("Empty ID");
    }

    for (iter = begin(); iter != end() && id != (*iter)->get_id(); ++iter)
        ;

    if (iter == end()) {
        if (!might_fail) {
            ValueNode::breakpoint();
        }

        throw Exception::IDNotFound("ValueNode in ValueNodeList: " + id);
    }

    return *iter;
}

ValueNode::ConstHandle
ValueNodeList::find(const String &id, bool might_fail)const
{
    const_iterator iter;

    if (id.empty()) {
        throw Exception::IDNotFound("Empty ID");
    }

    for (iter = begin(); iter != end() && id != (*iter)->get_id(); ++iter)
        ;

    if (iter == end()) {
        if (!might_fail) {
            ValueNode::breakpoint();
        }

        throw Exception::IDNotFound("ValueNode in ValueNodeList: " + id);
    }

    return *iter;
}

ValueNode::Handle
ValueNodeList::surefind(const String &id)
{
    if (id.empty()) {
        throw Exception::IDNotFound("Empty ID");
    }

    ValueNode::Handle value_node;

    try {
        value_node = find(id, true);
    } catch (Exception::IDNotFound) {
        value_node = PlaceholderValueNode::create();
        value_node->set_id(id);
        push_back(value_node);
        placeholder_count_++;
    }

    return value_node;
}

bool
ValueNodeList::erase(ValueNode::Handle value_node)
{
    assert(value_node);

    iterator iter;

    for (iter = begin(); iter != end(); ++iter)
        if (value_node.get() == iter->get()) {
            std::list<ValueNode::RHandle>::erase(iter);

            if (PlaceholderValueNode::Handle::cast_dynamic(value_node)) {
                placeholder_count_--;
            }

            return true;
        }

    return false;
}

bool
ValueNodeList::add(ValueNode::Handle value_node)
{
    if (!value_node) {
        return false;
    }

    if (value_node->get_id().empty()) {
        return false;
    }

    try {
        ValueNode::RHandle other_value_node = find(value_node->get_id(), true);

        if (PlaceholderValueNode::Handle::cast_dynamic(other_value_node)) {
            other_value_node->replace(value_node);
            placeholder_count_--;
            return true;
        }

        return false;
    } catch (Exception::IDNotFound) {
        push_back(value_node);
        return true;
    }

    return false;
}

void
ValueNodeList::audit()
{
    iterator iter, next;

    for (next = begin(), iter = next++; iter != end(); iter = next++)
        if (iter->count() == 1) {
            std::list<ValueNode::RHandle>::erase(iter);
        }
}

String
PlaceholderValueNode::get_name()const
{
    return "placeholder";
}

String
PlaceholderValueNode::get_local_name()const
{
    return _("Placeholder");
}

String
PlaceholderValueNode::get_string()const
{
    return String("PlaceholderValueNode: ") + get_guid().get_string();
}

ValueNode::Handle
PlaceholderValueNode::clone(Canvas::LooseHandle canvas, const GUID& deriv_guid)const
{
    ValueNode* ret(new PlaceholderValueNode());
    ret->set_guid(get_guid()^deriv_guid);
    ret->set_parent_canvas(canvas);
    return ret;
}

PlaceholderValueNode::Handle
PlaceholderValueNode::create(Type &type)
{
    if (getenv("SYNFIG_DEBUG_PLACEHOLDER_VALUENODE")) {
        printf("%s:%d PlaceholderValueNode::create\n", __FILE__, __LINE__);
    }

    return new PlaceholderValueNode(type);
}

ValueBase
PlaceholderValueNode::operator()(Time /*t*/)const
{
    assert(0);
    return ValueBase();
}

PlaceholderValueNode::PlaceholderValueNode(Type &type):
    ValueNode(type)
{
}

ValueNode::Handle
LinkableValueNode::clone(Canvas::LooseHandle canvas, const GUID& deriv_guid)const
{
    {
        ValueNode* x(find_value_node(get_guid()^deriv_guid).get());

        if (x) {
            return x;
        }
    }

    int i;
    LinkableValueNode *ret = create_new();
    ret->set_guid(get_guid()^deriv_guid);

    for (i = 0; i < link_count(); i++) {
        ValueNode::Handle link = get_link_vfunc(i);

        if (!link->is_exported()) {
            ValueNode::Handle value_node(find_value_node(link->get_guid()^deriv_guid));

            if (!value_node) {
                value_node = link->clone(canvas, deriv_guid);
            }

            ret->set_link(i, value_node);
        } else {
            ret->set_link(i, link);
        }
    }

    ret->set_parent_canvas(canvas);
    return ret;
}

String
ValueNode::get_relative_id(etl::loose_handle<const Canvas> x)const
{
    assert(is_exported());
    assert(canvas_);

    if (x.get() == canvas_.get()) {
        return get_id();
    }

    return canvas_->_get_relative_id(x) + ':' + get_id();
}

etl::loose_handle<Canvas>
ValueNode::get_parent_canvas()const
{
    if (getenv("SYNFIG_DEBUG_GET_PARENT_CANVAS")) {
        printf("%s:%d get_parent_canvas of %lx is %lx\n", __FILE__, __LINE__, uintptr_t(this), uintptr_t(canvas_.get()));
    }

    return canvas_;
}

etl::loose_handle<Canvas>
ValueNode::get_root_canvas()const
{
    if (getenv("SYNFIG_DEBUG_GET_PARENT_CANVAS")) {
        printf("%s:%d get_root_canvas of %lx is %lx\n", __FILE__, __LINE__, uintptr_t(this), uintptr_t(root_canvas_.get()));
    }

    return root_canvas_;
}

etl::loose_handle<Canvas>
ValueNode::get_non_inline_ancestor_canvas()const
{
    etl::loose_handle<Canvas> parent(get_parent_canvas());

    if (parent) {
        etl::loose_handle<Canvas> ret(parent->get_non_inline_ancestor());

        if (getenv("SYNFIG_DEBUG_GET_PARENT_CANVAS")) {
            printf("%s:%d get_non_inline_ancestor_canvas of %lx is %lx\n", __FILE__, __LINE__, uintptr_t(this), uintptr_t(ret.get()));
        }

        return ret;
    }

    return parent;
}

void
ValueNode::set_parent_canvas(etl::loose_handle<Canvas> x)
{
    if (getenv("SYNFIG_DEBUG_SET_PARENT_CANVAS")) {
        printf("%s:%d set_parent_canvas of %lx to %lx\n", __FILE__, __LINE__, uintptr_t(this), uintptr_t(x.get()));
    }

    canvas_ = x;

    if (getenv("SYNFIG_DEBUG_SET_PARENT_CANVAS")) {
        printf("%s:%d now %lx\n", __FILE__, __LINE__, uintptr_t(canvas_.get()));
    }

    if (x) {
        set_root_canvas(x);
    }
}

void
ValueNode::set_root_canvas(etl::loose_handle<Canvas> x)
{
    if (getenv("SYNFIG_DEBUG_SET_PARENT_CANVAS")) {
        printf("%s:%d set_root_canvas of %lx to %lx - ", __FILE__, __LINE__, uintptr_t(this), uintptr_t(x.get()));
    }

    root_canvas_ = x->get_root();

    if (getenv("SYNFIG_DEBUG_SET_PARENT_CANVAS")) {
        printf("now %lx\n", uintptr_t(root_canvas_.get()));
    }
}

String
ValueNode::get_string()const
{
    return String("ValueNode: ") + get_description();
}

void LinkableValueNode::get_times_vfunc(Node::time_set &set) const
{
    ValueNode::LooseHandle	h;

    int size = link_count();

    // just add it to the set...
    for (int i = 0; i < size; ++i) {
        h = get_link(i);

        if (h) {
            const Node::time_set &tset = h->get_times();
            set.insert(tset.begin(), tset.end());
        }
    }
}

String
LinkableValueNode::get_description(int index, bool show_exported_name)const
{
    String description;

    if (index == -1) {
        description = strprintf(" Linkable ValueNode (%s)", get_local_name().c_str());

        if (show_exported_name && is_exported()) {
            description += strprintf(" (%s)", get_id().c_str());
        }
    } else {
        description = String(":") + link_local_name(index);

        if (show_exported_name) {
            ValueNode::LooseHandle link(get_link(index));

            if (link->is_exported()) {
                description += strprintf(" (%s)", link->get_id().c_str());
            }
        }
    }

    const synfig::Node* node = this;
    LinkableValueNode::ConstHandle parent_linkable_vn = 0;

    // walk up through the valuenodes trying to find the layer at the top
    while (!node->parent_set.empty() && !dynamic_cast<const Layer*>(node)) {
        LinkableValueNode::ConstHandle linkable_value_node(dynamic_cast<const LinkableValueNode*>(node));

        if (linkable_value_node) {
            String link;
            int cnt = linkable_value_node->link_count();

            for (int i = 0; i < cnt; i++)
                if (linkable_value_node->get_link(i) == parent_linkable_vn) {
                    link = String(":") + linkable_value_node->link_local_name(i);
                    break;
                }

            description = linkable_value_node->get_local_name() + link + (parent_linkable_vn ? ">" : "") + description;
        }

        node = *node->parent_set.begin();
        parent_linkable_vn = linkable_value_node;
    }

    Layer::ConstHandle parent_layer(dynamic_cast<const Layer*>(node));

    if (parent_layer) {
        String param;
        const Layer::DynamicParamList &dynamic_param_list(parent_layer->dynamic_param_list());

        // loop to find the parameter in the dynamic parameter list - this gives us its name
        for (Layer::DynamicParamList::const_iterator iter = dynamic_param_list.begin(); iter != dynamic_param_list.end(); iter++)
            if (iter->second == parent_linkable_vn) {
                param = String(":") + parent_layer->get_param_local_name(iter->first);
            }

        description = strprintf("(%s)%s>%s",
                                parent_layer->get_non_empty_description().c_str(),
                                param.c_str(),
                                description.c_str());
    }

    return description;
}

String
LinkableValueNode::get_description(bool show_exported_name)const
{
    return get_description(-1, show_exported_name);
}

String
LinkableValueNode::link_name(int i)const
{
    Vocab vocab(get_children_vocab());
    Vocab::iterator iter(vocab.begin());
    int j = 0;

    for (; iter != vocab.end() && j < i; iter++, j++) {};

    return iter != vocab.end() ? iter->get_name() : String();
}

String
LinkableValueNode::link_local_name(int i)const
{
    Vocab vocab(get_children_vocab());
    Vocab::iterator iter(vocab.begin());
    int j = 0;

    for (; iter != vocab.end() && j < i; iter++, j++) {};

    return iter != vocab.end() ? iter->get_local_name() : String();
}

int
LinkableValueNode::get_link_index_from_name(const String &name)const
{
    Vocab vocab(get_children_vocab());
    Vocab::iterator iter(vocab.begin());
    int j = 0;

    for (; iter != vocab.end(); iter++, j++)
        if (iter->get_name() == name) {
            return j;
        }

    throw Exception::BadLinkName(name);
}

int
LinkableValueNode::link_count()const
{
    return get_children_vocab().size();
}

LinkableValueNode::Vocab
LinkableValueNode::get_children_vocab()const
{
    return get_children_vocab_vfunc();
}

void
LinkableValueNode::set_children_vocab(const Vocab &newvocab)
{
    children_vocab.assign(newvocab.begin(), newvocab.end());
}

void
LinkableValueNode::set_root_canvas(etl::loose_handle<Canvas> x)
{
    ValueNode::set_root_canvas(x);

    for (int i = 0; i < link_count(); ++i) {
        get_link(i)->set_root_canvas(x);
    }
}

void
LinkableValueNode::get_values_vfunc(std::map<Time, ValueBase> &x) const
{
    std::set<Time> times;

    for (int i = 0; i < link_count(); ++i)
        if (ValueNode::Handle link = get_link(i)) {
            link->get_value_change_times(times);
        }

    for (std::set<Time>::const_iterator i = times.begin(); i != times.end(); ++i) {
        add_value_to_map(x, *i, (*this)(*i));
    }
}